local Util = require('skywalking.util')
local Span = require('skywalking.span')
local CorrelationContext = require('skywalking.correlation_context')
local Const = require('skywalking.constants')
local Segment = require('skywalking.segment')

local _M = {}

-- local TracingContext = {
--     trace_id,
--     segment_id,
--     service,
--     service_instance,
--     is_noop = false,   
--     sample,     是否需要采样
--     correlation_context
--     active_count
--     finished_spans
--     -- span id starts from 0
--     span_id_seq,
--     owner,
--     -- The first created span.
--     first_span,
--     -- Created span and still active
--     active_spans,
--     active_count,
--     -- Finished spans
--     finished_spans,
-- }

function _M.newNoOP()
    return {is_noop = true}
end

function _M.new(serviceName, serviceInstanceName)
    if serviceInstanceName == nil or serviceName == nil then
        return _M.newNoOP()
    end

    local tracing_context = Util.tablepool_fetch()
    tracing_context.trace_id = Util.newID()
    tracing_context.segment_id =  tracing_context.trace_id --tracing_context.trace_id
    tracing_context.service = serviceName
    tracing_context.service_instance = serviceInstanceName
    tracing_context.sample = 1
    
    tracing_context.correlation_context = CorrelationContext.new()
    tracing_context.span_id_seq = 0
    tracing_context.active_spans = Util.tablepool_fetch()
    tracing_context.active_count = 0
    tracing_context.finished_spans = Util.tablepool_fetch()

    return setmetatable(tracing_context, {  __index = _M  })
end

function _M.addRefIfFirst(tracingContext, ref)
    tracingContext.trace_id = ref.trace_id
    -- 设置来自上游的采样开关值(这个暂时不用，官方没有强制使用)
    tracingContext.sample = ref.sample or tracingContext.sample
end

function _M.addActive(tracingContext, span)
    if tracingContext.first_span == nil then
        tracingContext.first_span = span
    end

    tracingContext.active_spans[span.span_id + 1] = span
    tracingContext.active_count = tracingContext.active_count + 1
end

function _M.finishSpan(tracingContext, span)
    tracingContext.active_spans[span.span_id + 1] = nil
    tracingContext.active_count = tracingContext.active_count - 1
    tracingContext.finished_spans[#tracingContext.finished_spans + 1] = span
    if tracingContext.active_count == 0 then
        tracingContext.is_finished = true
    end
end

--- Generate the next span ID.
function _M.nextSpanID(tracingContext)
    local nextSpanId = tracingContext.span_id_seq
    tracingContext.span_id_seq = tracingContext.span_id_seq + 1
    return nextSpanId
end

--- is finished
function _M.finished(tracingContext)
    return tracingContext.is_finished
end

function _M.Report(tracingContext, tracer)
    if not tracingContext.is_finished then
        tracer.finish()
    end

    if tracingContext.sample == 1 then
        local ok, segment = tracingContext:get_segment()
        if not ok then
            return
        end
        
        tracer.reporter:send(Segment.transform(segment))
    end
end

function _M.get_segment(tracingContext)
    if tracingContext.is_noop then
        return false, nil
    end

    if tracingContext.active_count ~= 0 then
        return false, nil
    else
        local segment = Util.tablepool_fetch()
        segment.trace_id = tracingContext.trace_id
        segment.segment_id = tracingContext.segment_id
        segment.service = tracingContext.service
        segment.service_instance = tracingContext.service_instance
        segment.spans = tracingContext.finished_spans
        return true, segment
    end
end

return _M
